
#include <string.h>
#include "stm32f4xx.h"
#include "board.h"

/* the other one channel is used for injected channel */
static uint16_t adc_value[ADC_CHANNEL_NUM - 1] = {0};
static uint16_t adc_ready_value[ADC_CHANNEL_NUM] = {0};

static void _adc1_init(void)
{
    ADC_InitTypeDef ADC_InitStructure;
    ADC_CommonInitTypeDef ADC_CommonInitStructure;
    DMA_InitTypeDef DMA_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    /* DMA2 Stream0 configuration for ADC1 */
    DMA_InitStructure.DMA_Channel = DMA_Channel_0;
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;
    DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)adc_value;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
    DMA_InitStructure.DMA_BufferSize = ADC_CHANNEL_NUM - 1;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;
    DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
    DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
    DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
    DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
    DMA_Init(DMA2_Stream0, &DMA_InitStructure);

    /* Enable DMA2 Stream0 */
    DMA_Cmd(DMA2_Stream0, ENABLE);

    /* Configure ADC common parameters */
    ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;
    ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div8;
    ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
    ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
    ADC_CommonInit(&ADC_CommonInitStructure);

    /* Configure ADC1 */
    ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
    ADC_InitStructure.ADC_ScanConvMode = ENABLE;
    ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
    ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T4_CC4;
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
    ADC_InitStructure.ADC_NbrOfConversion = ADC_CHANNEL_NUM - 1;
    ADC_Init(ADC1, &ADC_InitStructure);

    /* Configure ADC channels */
    ADC_RegularChannelConfig(ADC1, ADC_Channel_Vbat, 1, ADC_SampleTime_56Cycles);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_TempSensor, 2, ADC_SampleTime_56Cycles);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_Vrefint, 3, ADC_SampleTime_56Cycles);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 4, ADC_SampleTime_56Cycles);

    ADC_VBATCmd(ENABLE);
    ADC_TempSensorVrefintCmd(ENABLE);

    /* Enable DMA for ADC1 */
    ADC_DMACmd(ADC1, ENABLE);
    ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE);

    /* Injected Channel */
    ADC_InjectedSequencerLengthConfig(ADC1, 1);
    ADC_InjectedChannelConfig(ADC1, ADC_Channel_5, 1, ADC_SampleTime_56Cycles);
    ADC_ExternalTrigInjectedConvConfig(ADC1, ADC_ExternalTrigInjecConv_T4_CC1);
    ADC_ExternalTrigInjectedConvEdgeConfig(ADC1, ADC_ExternalTrigInjecConvEdge_Rising);

    /* Configure interrupts */
    NVIC_InitStructure.NVIC_IRQChannel = ADC_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream0_IRQn;
    NVIC_Init(&NVIC_InitStructure);

    ADC_ITConfig(ADC1, ADC_IT_JEOC, ENABLE);
    DMA_ITConfig(DMA2_Stream0, DMA_IT_TC, ENABLE);

    /* Enable ADC1 */
    ADC_Cmd(ADC1, ENABLE);
}

/**
 * @brief ADC interrupt handler
 */
void ADC_IRQHandler(void)
{
    /* Check if end of conversion interrupt occurred */
    if (ADC_GetITStatus(ADC1, ADC_IT_JEOC))
    {
        uint32_t primask = __get_PRIMASK();
        __disable_irq();
        adc_ready_value[ADC_CHANNEL_NUM - 1] = ADC_GetInjectedConversionValue(ADC1, ADC_InjectedChannel_1);
        __set_PRIMASK(primask);
        /* Clear interrupt flag */
        ADC_ClearITPendingBit(ADC1, ADC_IT_JEOC);
    }
}

/**
 * @brief DMA2 Stream0 interrupt handler
 */
void DMA2_Stream0_IRQHandler(void)
{
    if (DMA_GetITStatus(DMA2_Stream0, DMA_IT_TCIF0))
    {
        uint32_t primask = __get_PRIMASK();
        __disable_irq();
        memcpy(adc_ready_value, adc_value, sizeof(adc_value));
        __set_PRIMASK(primask);
        /* Clear interrupt flag */
        DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_TCIF0);
    }
}

/**
 * @brief Initialize TIM4 for PWM generation and ADC triggering
 *
 * Configuration:
 * - Timer period: 1ms (1kHz)
 * - Channel 1: Direct PWM output (PB6)
 * - No interrupts used
 */
static void _timer4_init(void)
{
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    TIM_OCInitTypeDef TIM_OCInitStructure;

    TIM_TimeBaseStructure.TIM_Prescaler = 84 - 1;   // 84MHz / 84 = 1MHz
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseStructure.TIM_Period = 1000 - 1;    // 1000 counts = 1ms at 1MHz
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
    TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);

    /* Configure TIM4 Channel 1 for PWM output */
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Disable;
    TIM_OCInitStructure.TIM_Pulse = 500;    // 50% duty cycle (500/1000)
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
    TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;
    TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset;
    TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset;

    TIM_OC1Init(TIM4, &TIM_OCInitStructure);
    TIM_OC1PreloadConfig(TIM4, TIM_OCPreload_Enable);

    /* Configure TIM4 Channel 3 for ADC injected trigger */
    TIM_OCInitStructure.TIM_Pulse = 100;    // 10% duty cycle
    TIM_OC3Init(TIM4, &TIM_OCInitStructure);
    TIM_OC3PreloadConfig(TIM4, TIM_OCPreload_Enable);

    /* Configure TIM4 Channel 4 for ADC regular trigger */
    // Set pulse width between Channel 3 and Channel 1
    TIM_OCInitStructure.TIM_Pulse = 900;    // 90% duty cycle
    TIM_OC4Init(TIM4, &TIM_OCInitStructure);
    TIM_OC4PreloadConfig(TIM4, TIM_OCPreload_Enable);

    /* Configure TIM4 trigger outputs for ADC */
    /* Master Mode Configuration - Generate TRGO on Update for other peripherals if needed */
    TIM_SelectMasterSlaveMode(TIM4, TIM_MasterSlaveMode_Enable);
    TIM_SelectOutputTrigger(TIM4, TIM_TRGOSource_Update);

    TIM_ARRPreloadConfig(TIM4, ENABLE);
    TIM_Cmd(TIM4, ENABLE);
    TIM_CtrlPWMOutputs(TIM4, ENABLE);
}

int driv_timer_adc_init(void)
{
    _timer4_init();
    _adc1_init();
    return 0;
}

int driv_adc_get_value(uint16_t* val)
{
    if (!val) {
        return -1;
    }
    uint32_t primask = __get_PRIMASK();
    __disable_irq();
    for (int i = 0; i < ADC_CHANNEL_NUM; i++)
    {
        val[i] = adc_ready_value[i];
    }
    __set_PRIMASK(primask);

    return 0;
}

int driv_timer_pwm_set(uint16_t pwm_value)
{
    uint16_t pulse_value;
    pulse_value = (uint16_t)(pwm_value / 10);
    TIM4->CCR1 = pulse_value;

    return 0;
}
